﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BuildPlannerLib;
using BuildPlannerLib.Buildings;
using BuildPlannerLib.Enums;
using BuildPlannerLib.PlayerActions;
using BuildPlannerLib.Terran;
using BuildPlannerLib.Terran.Buildings;
using BuildPlannerLib.Terran.Enums;
using BuildPlannerLib.Terran.PlayerActions;

namespace BuildPlannerTest
{
    class Program
    {
        /// <summary>
        /// Reads commands 'terran', 'zerg', and 'protoss' to determine whice race simulation will be run.
        /// </summary>
        /// <param name="build"></param>
        public string RaceSelect()
        {
            Console.WriteLine("Select the race to be used in the simulation: \"terran\", \"zerg\", or \"protoss\"\nTo stop, submit exit or quit.");
            Console.Write(">");
            string command = Console.ReadLine().ToLower();
            Console.WriteLine();

            while (command != "exit" && command != "quit" && command != "terran" && command != "t")
            {
                string baseCommand = command.Split(' ')[0];
                switch (baseCommand)
                {
                    case "zerg":
                        Console.WriteLine("Zerg race not implemented.\nPlease select another race.\n");
                        break;
                    case "z":
                        Console.WriteLine("Zerg race not implemented.\nPlease select another race.\n");
                        break;
                    case "protoss":
                        Console.WriteLine("Protoss race not implemented.\nPlease select another race.\n");
                        break;
                    case "p":
                        Console.WriteLine("Protoss race not implemented.\nPlease select another race.\n");
                        break;
                    default:
                        Console.WriteLine("Invalid race.\nSelect the race to be used in the simulation: \"terran\", \"zerg\", or \"protoss\"\n");
                        break;
                }
                Console.Write(">");
                command = Console.ReadLine().ToLower();
                Console.WriteLine();
            }
            return command;
        }

        /// <summary>
        /// Reads commands 'custom' and 'pre-built' to determine what type of simulation will be run.
        /// </summary>
        /// <param name="build"></param>
        public void ReadCommands(Build build)
        {
            Console.WriteLine("Select \"custom\" or \"pre-built\" session.\nTo stop, submit \"exit\" or \"quit\".");
            Console.Write(">>");
            string command = Console.ReadLine().ToLower();
            Console.WriteLine();

            while (command != "exit" && command != "quit")
            {
                string baseCommand = command.Split(' ')[0];
                switch (baseCommand)
                {
                    case "custom":
                        ParseCommands(build);
                        break;
                    case "pre-built":
                        SendCommands("Test1", build);
                        break;
                    default:
                        Console.WriteLine("Invalid command.\nCurrent commands include \"custom\" or \"pre-built\".");
                        break;
                }

                if (command == "custom" || command == "pre-built")
                {
                    return;
                }

                Console.Write(">>");
                command = Console.ReadLine().ToLower();
                Console.WriteLine();
            }
        }

        /// <summary>
        /// Sends commands in an automation fashion from a file. 
        /// Reads each line and passes the command on that line.
        /// </summary>
        /// <param name="testRun">Name of the file stored as a Content File.</param>
        /// <param name="build">Build.</param>
        private void SendCommands(string testRun, Build build)
        {
            try
            {
                using (StreamReader sr = new StreamReader(@"TestRuns\" + testRun + ".txt"))
                {
                    String text = sr.ReadToEnd();
                    foreach (string line in text.Split('\n'))
                    {
                        //Console.WriteLine(line);
                        ParseCommand(build, line);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Could not read the file.\n" + ex.Message);
            }
        
        }

        /// <summary>
        /// Wrapper to allow custom commands to be entered repeatedly.
        /// </summary>
        /// <param name="build"></param>
        public void ParseCommands(Build build)
        {
            Console.WriteLine("Input commands below. Type \"help\" or \"commands\" for a list of commands\navailable.");
            Console.Write(">>>");
            string command = Console.ReadLine().ToLower();

            while (command != "exit" && command != "quit")
            {
                ParseCommand(build, command);
                Console.Write(">>>");
                command = Console.ReadLine().ToLower();
            } 
        }

        /// <summary>
        /// Interprets a command and issues the appropriate method call.
        /// </summary>
        /// <param name="build">Build.</param>
        /// <param name="command">Command as a string.</param>
        public void ParseCommand(Build build, string command)
        {
            // Clean up the command            
            command = command.ToLower();
            command = command.Replace("\r", "");
            command = command.Replace("\n", "");

            // Split the command into an array.
            string[] commandArray = command.Split(' ');

            Console.Write("");

            int baseNumber;
            int time;
            int fromBase;
            int toBase;

            switch (commandArray[0])
            {
                case "help":
                    Help(build);
                    break;
                case "commands":
                    Help(build);
                    break;
                case "status":
                    Status(build);
                    break;
                case "status-actions":
                    StatusAction(build);
                    break;
                case "status-bases":
                    StatusBases(build);
                    break;
                case "status-buildings":
                    StatusBuildings(build);
                    break;
                case "status-units":
                    StatusUnits(build);
                    break;
                case "status-upgrades":
                    StatusUpgrades(build);
                    break;
                case "add-time":
                    if (commandArray.Length != 2)
                    {
                        Console.WriteLine("Invalid command.\n\"add-time\" command must only include one integer argument.\n");
                        break;
                    }
                    int timeDifference;
                    try
                    {
                        timeDifference = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"add-time\" command must only include one integer argument.\n");
                        break;
                    }
                    AddTime(build, timeDifference);
                    break;
                case "set-time":
                    if (commandArray.Length != 2)
                    {
                        Console.WriteLine("Invalid command.\n\"set-time\" command must only include one integer argument.\n");
                        break;
                    }
                    int finalTime;
                    try
                    {
                        finalTime = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"set-time\" command must only include one integer argument.\n");
                        break;
                    }
                    SetTime(build, finalTime);
                    break;
                case "remove-action":
                    if (commandArray.Length != 2)
                    {
                        Console.WriteLine("Invalid command.\n\"set-time\" command must only include one integer argument.\n");
                        break;
                    }
                    int actionIndex;
                    try
                    {
                        actionIndex = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"set-time\" command must only include one integer argument.\n");
                        break;
                    }
                    RemoveAction(build, actionIndex);
                    break;
                case "add-worker":
                    AddWorker(build);
                    break;
                case "add-unit":
                    if (commandArray.Length != 2)
                    {
                        Console.WriteLine("Invalid command.\n\"add-unit\" command must only include the unit name with no spaces.\n");
                        break;
                    }
                    AddUnit(build, commandArray[1]);
                    break;
                case "add-building":
                    if (commandArray.Length != 3)
                    {
                        Console.WriteLine("Invalid command.\n\"add-building\" command must only include the building name with no spaces and\nthe base number.\n");
                        break;
                    }
                    try
                    {
                        baseNumber = Convert.ToInt32(commandArray[2]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"add-building\" command must only include the building name with no spaces and\nthe base number.\n");
                        break;
                    }
                    AddBuilding(build, commandArray[1], baseNumber);
                    break;
                case "float-cc":
                    if (commandArray.Length != 3)
                    {
                        Console.WriteLine("Invalid command.\n\"transfer-worker\" command must only include three integer arguments.\n");
                        break;
                    }
                    try
                    {
                        toBase = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"transfer-worker\" command must only include three integer arguments.\n");
                        break;
                    }
                    try
                    {
                        time = Convert.ToInt32(commandArray[2]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"transfer-worker\" command must only include three integer arguments.\n");
                        break;
                    }
                    FloatCC(build, toBase, time);
                    break;
                case "salvage-bunker":
                    SalvageBunker(build);
                    break;
                case "add-upgrade":
                    if (commandArray.Length != 2)
                    {
                        Console.WriteLine("Invalid command.\n\"add-upgrade\" command must only include the unit name with no spaces.\n");
                        break;
                    }
                    AddUpgrade(build, commandArray[1]);
                    break;
                case "add-addon":
                    if (commandArray.Length != 3)
                    {
                        Console.WriteLine("Invalid command.\n\"add-addon\" command must only include the building name with no spaces and the\naddon type with no spaces.\n");
                        break;
                    }
                    AddAddon(build, commandArray[1], commandArray[2]);
                    break;
                case "remove-addon":
                    if (commandArray.Length != 3)
                    {
                        Console.WriteLine("Invalid command.\n\"remove-addon\" command must only include the building name with no spaces and\nthe addon type with no spaces.\n");
                        break;
                    }
                    RemoveAddon(build, commandArray[1], commandArray[2]);
                    break;
                case "attach-addon":
                    if (commandArray.Length != 3)
                    {
                        Console.WriteLine("Invalid command.\n\"attach-addon\" command must only include the building name with no spaces and\nthe addon type with no spaces.\n");
                        break;
                    }
                    AttachAddon(build, commandArray[1], commandArray[2]);
                    break;
                case "orbital":
                    Orbital(build);
                    break;
                case "planetary":
                    Planetary(build);
                    break;
                case "supplycalldown":
                    SupplyCalldown(build);
                    break;
                case "scan":
                    Scan(build);
                    break;
                case "creep":
                    Creep(build);
                    break;
                case "chronoboost":
                    Chronoboost(build);
                    break;
                case "convert-wg":
                    ConvertWarpgate(build);
                    break;
                case "expand":
                    if (commandArray.Length != 3)
                    {
                        Console.WriteLine("Invalid command.\n\"expand\" command must only include two integer argument.\n");
                        break;
                    }
                    int workerBaseNumber;
                    try
                    {
                        workerBaseNumber = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"expand\" command must only include two integer argument.\n");
                        break;
                    }
                    try
                    {
                        baseNumber = Convert.ToInt32(commandArray[2]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"expand\" command must only include two integer argument.\n");
                        break;
                    }
                    Expand(build, workerBaseNumber, baseNumber);
                    break;
                case "transfer-worker":
                    if (commandArray.Length != 4)
                    {
                        Console.WriteLine("Invalid command.\n\"transfer-worker\" command must only include three integer arguments.\n");
                        break;
                    }
                    try
                    {
                        fromBase = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"transfer-worker\" command must only include three integer arguments.\n");
                        break;
                    }
                    try
                    {
                        toBase = Convert.ToInt32(commandArray[2]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"transfer-worker\" command must only include three integer arguments.\n");
                        break;
                    }
                    try
                    {
                        time = Convert.ToInt32(commandArray[3]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"transfer-worker\" command must only include three integer arguments.\n");
                        break;
                    }
                    TransferWorker(build, fromBase, toBase, time);
                    break;
                case "worker-to-minerals":
                    if (commandArray.Length != 2)
                    {
                        Console.WriteLine("Invalid command.\n\"worker-to-minerals\" command must only include one integer argument.\n");
                        break;
                    }
                    try
                    {
                        baseNumber = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"worker-to-minerals\" command must only include one integer argument.\n");
                        break;
                    }
                    WorkerToMinerals(build, baseNumber);
                    break;
                case "worker-to-gas":
                    if (commandArray.Length != 2)
                    {
                        Console.WriteLine("Invalid command.\n\"worker-to-gas\" command must only include one integer argument.\n");
                        break;
                    }
                    try
                    {
                        baseNumber = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"worker-to-gas\" command must only include one integer argument.\n");
                        break;
                    }
                    WorkerToGas(build, baseNumber);
                    break;
                case "idle-worker" :
                    if (commandArray.Length != 3)
                    {
                        Console.WriteLine("Invalid command.\n\"idle-worker\" command must only include two integer arguments.\n");
                        break;
                    }
                    try
                    {
                        baseNumber = Convert.ToInt32(commandArray[1]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"idle-worker\" command must only include two integer arguments.\n");
                        break;
                    }
                    try
                    {
                        time = Convert.ToInt32(commandArray[2]);
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("Invalid command.\n\"idle-worker\" command must only include two integer arguments.\n");
                        break;
                    }
                    IdleWorker(build, baseNumber, time);
                    break;
                default:
                    Console.WriteLine("Unrecognized command: \"" + command + "\".\n");
                    break;
            }
        }

        public void Status(Build build)
        {
            Console.WriteLine("\n **** Status ****\n");
            TableBuilder statusTable = new TableBuilder();

            statusTable.AddRow("Time:", build.CurrentTime.ToString(@"hh\:mm\:ss\.ff"));
            statusTable.AddRow("Minerals:", build.Player.Minerals.Amount);
            statusTable.AddRow("Gas:", build.Player.Gas.Amount);
            statusTable.AddRow("Supply:", build.Player.Supply.Amount + "/" + build.Player.SupplyMaximum.Amount);
            statusTable.AddRow("Workers:", build.Workers);

            Console.Write(statusTable.Output());
            Console.WriteLine();
        }

        public void StatusAction(Build build)
        {
            Console.WriteLine("\n **** Action List Status ****\n");
            if (build.ActionList.Count() == 0)
                Console.WriteLine("No actions.\n");
            else
            {
                TableBuilder actionStatusTable = new TableBuilder();
                foreach(PlayerAction a in build.ActionList)
                {
                    if (a.StartTime == null || a.EndTime == null)
                        actionStatusTable.AddRow("Action " + build.ActionList.IndexOf(a), a.Status, " ", " ");
                    else
                        actionStatusTable.AddRow("Action " + build.ActionList.IndexOf(a), a.Status, "StartTime: " + a.StartTime.Value.ToString("g"), "EndTime: " + a.EndTime.Value.ToString("g"));
                }
                StringBuilder finalString = new StringBuilder();
                int i = 0;
                foreach (ITextRow tr in actionStatusTable)
                {
                    tr.Output(finalString);
                    finalString.AppendLine(build.ActionList.ElementAt(i).LongDescription);
                    finalString.AppendLine();
                    i++;
                }
                Console.Write(finalString.ToString());
            }
        }

        public void StatusBases(Build build)
        {
            int[] baseHeaderIndex = new int[build.Map.Bases.Count()];
            TableBuilder baseStatusTable = new TableBuilder();
            Console.WriteLine("\n **** Base Status ****");
            build.Map.Bases.ForEach(delegate(Base b)
            {
                baseHeaderIndex[build.Map.Bases.IndexOf(b)] = baseStatusTable.Count();
                baseStatusTable.AddRow("Status:", b.Status);
                baseStatusTable.AddRow(" ", " ");

                int mineralPatches = (b.MineralPatches[0] + b.MineralPatches[2]);
                if (mineralPatches > 0)
                    baseStatusTable.AddRow("Mineral Patches:", mineralPatches + " (" + b.MineralPatches[2] + " close)");

                int goldMineralPatches = (b.MineralPatches[1] + b.MineralPatches[3]);
                if (goldMineralPatches > 0)
                    baseStatusTable.AddRow("Gold Mineral Patches:", goldMineralPatches + " (" + b.MineralPatches[3] + " close)");

                if (b.GasGeysers[0] > 0)
                    baseStatusTable.AddRow("Gas Geysers:", b.GasGeysers[0] + " (" + b.ConvertedGasGeysers[0] + " converted)");

                if (b.GasGeysers[1] > 0)
                    baseStatusTable.AddRow("Rich Gas Geysers:", b.GasGeysers[1] + " (" + b.ConvertedGasGeysers[1] + " converted)");

                if (b.GasGeysers[2] > 0)
                    baseStatusTable.AddRow("8 Yield Gas Geysers:", b.GasGeysers[2] + " (" + b.ConvertedGasGeysers[2] + " converted)");

                if (b.Status == BaseStatus.ExpandFinished)
                {
                    baseStatusTable.AddRow(" ", " ");
                    baseStatusTable.AddRow("Mineral Workers:", b.MineralWorkers);
                    baseStatusTable.AddRow("Gas Workers:", b.GasWorkers);
                }
            });
            
            StringBuilder finalString = new StringBuilder();
            int i = 0;
            int j = 0;
            foreach (ITextRow tr in baseStatusTable)
            {
                if (j < baseHeaderIndex.Count() && baseHeaderIndex[j] == i)
                {
                    finalString.AppendLine();
                    finalString.AppendLine(" **** Base " + j + " ****");
                    finalString.AppendLine();
                    j++;
                }

                tr.Output(finalString);
                i++;
            }
            Console.Write(finalString.ToString());
            Console.WriteLine();
        }

        public void StatusBuildings(Build build)
        {
            Console.WriteLine("\n **** Building Status ****\n");
            TableBuilder buildingStatusTable = new TableBuilder();
            switch (build.Race)
            {
                case Race.Terran:
                    TerranPlayer terranPlayer = build.Player as TerranPlayer;
                    CommandCenter commandCenter = build.Player.MainBuilding as CommandCenter;

                    int commandCenterInUse = commandCenter.IndividualMainBuildings.Count(delegate(IndividualMainBuilding i) { return i.InUse; });
                    buildingStatusTable.AddRow("Command Center:", 
                        commandCenter.IndividualMainBuildings.Count() 
                        + " (" 
                        + commandCenterInUse
                        + " in-use)");

                    int orbitalCommand = commandCenter.IndividualMainBuildings.Count(delegate(IndividualMainBuilding i)
                    {
                        IndividualCommandCenter c = i as IndividualCommandCenter;
                        return c.UpgradeStatus == CommandCenterUpgrade.OrbitalCommand;
                    });
                    if (orbitalCommand > 0)
                        buildingStatusTable.AddRow("Orbital Command:", orbitalCommand);
                    
                    int planetaryFortress = commandCenter.IndividualMainBuildings.Count(delegate(IndividualMainBuilding i)
                    {
                        IndividualCommandCenter c = i as IndividualCommandCenter;
                        return c.UpgradeStatus == CommandCenterUpgrade.PlanetaryFortress;
                    });
                    if (planetaryFortress > 0)
                        buildingStatusTable.AddRow("Planetary Fortress:", planetaryFortress);

                    buildingStatusTable.AddRow(" ", " ");

                    foreach (Building b in build.Player.Buildings)
                    {
                        if (b is AddonBuilding && b.GetCount() > 0)
                        {
                            AddonBuilding a = b as AddonBuilding;
                            
                            int addonBuildingInUse = a.IndividualBuildings.Count(delegate(IndividualBuilding i) { return i.InUse; });
                            buildingStatusTable.AddRow(a.Name + ":", a.GetCount() + " (" + addonBuildingInUse + " in-use)");
                            
                            int techLab = a.IndividualBuildings.Count(delegate(IndividualBuilding i)
                            {
                                IndividualAddonBuilding c = i as IndividualAddonBuilding;
                                return c.AddonType == AddonType.TechLab;
                            });
                            if (techLab > 0)
                                buildingStatusTable.AddRow("with Tech Lab:", techLab);
                            
                            int reactor = a.IndividualBuildings.Count(delegate(IndividualBuilding i)
                            {
                                IndividualAddonBuilding c = i as IndividualAddonBuilding;
                                return c.AddonType == AddonType.Reactor;
                            });
                            if (reactor > 0)
                                buildingStatusTable.AddRow("with Reactor", reactor);
                            buildingStatusTable.AddRow(" ", " ");
                        }
                    }

                    int previousCount = buildingStatusTable.Count();

                    foreach (Building b in build.Player.Buildings)
                    {
                        if (!(b is AddonBuilding) && b is ProductionBuilding && b.GetCount() > 0)
                        {
                            ProductionBuilding p = b as ProductionBuilding;

                            int productionInUse = p.IndividualBuildings.Count(delegate(IndividualBuilding i) { return i.InUse; });
                            buildingStatusTable.AddRow(p.Name + ":", p.GetCount() + " (" + productionInUse + " in-use)");
                        }
                    }

                    if (buildingStatusTable.Count() > previousCount)
                        buildingStatusTable.AddRow(" ", " ");

                    previousCount = buildingStatusTable.Count();

                    foreach (Building b in build.Player.Buildings)
                    {
                        if ((b is NonProduction || b is SupplyDepot) && b.GetCount() > 0)
                            buildingStatusTable.AddRow(b.Name + ":", b.GetCount());
                    }

                    if (terranPlayer.IdleTechLab > 0)
                        buildingStatusTable.AddRow("Idle Tech Labs:", terranPlayer.IdleTechLab);
                    if (terranPlayer.IdleReactor > 0)
                        buildingStatusTable.AddRow("Idle Reactors:", terranPlayer.IdleReactor);

                    if (buildingStatusTable.Count() > previousCount)
                        buildingStatusTable.AddRow(" ", " ");

                    Console.Write(buildingStatusTable.Output());
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    break;
            }
        }

        public void StatusUnits(Build build)
        {
            Console.WriteLine("\n **** Unit Status ****\n");
            TableBuilder unitStatusTable = new TableBuilder();
            switch (build.Race)
            {
                case Race.Terran:
                    foreach (Unit u in build.Player.Units)
                    {
                        if (u.ProducedFrom == "Barracks" && u.Count > 0)
                            unitStatusTable.AddRow(u.Name + ":", u.Count);
                    }

                    if (unitStatusTable.Count() > 0)
                        unitStatusTable.AddRow(" ", " ");

                    int previousCount = unitStatusTable.Count();
                    
                    foreach (Unit u in build.Player.Units)
                    {
                        if (u.ProducedFrom == "Factory" && u.Count > 0)
                        {
                            unitStatusTable.AddRow(u.Name + ":", u.Count);
                        }
                    }

                    if (unitStatusTable.Count() > previousCount)
                        unitStatusTable.AddRow(" ", " ");

                    foreach (Unit u in build.Player.Units)
                    {
                        if (u.ProducedFrom == "Starport" && u.Count > 0)
                        {
                            unitStatusTable.AddRow(u.Name + ":", u.Count);
                        }
                    }

                    if (unitStatusTable.Count() == 0)
                        Console.WriteLine("No units.");
                    else
                        Console.Write(unitStatusTable.Output());

                    Console.WriteLine();
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    break;
            }
        }

        public void StatusUpgrades(Build build)
        {
            Console.WriteLine("\n **** Upgrade Status ****\n");
            TableBuilder upgradeStatusTable = new TableBuilder();
            switch (build.Race)
            {
                case Race.Terran:
                    foreach (LevelUpgrade l in build.Player.LevelUpgrades)
                    {
                        if (l.ProducedFrom == "Engineering Bay" && l.LevelStarted > 0)
                        {
                            if (l.LevelStarted > l.LevelFinished)
                                upgradeStatusTable.AddRow(l.Name + ":", "Level " + l.LevelStarted + " Started");
                            else
                                upgradeStatusTable.AddRow(l.Name + ":", "Level " + l.LevelFinished + " Finished");
                        }
                    }

                    if (upgradeStatusTable.Count() > 0)
                        upgradeStatusTable.AddRow(" ", " ");

                    int previousCount = upgradeStatusTable.Count();
                    foreach (LevelUpgrade l in build.Player.LevelUpgrades)
                    {
                        if (l.ProducedFrom == "Armory" && l.LevelStarted > 0)
                        {
                            if (l.LevelStarted > l.LevelFinished)
                                upgradeStatusTable.AddRow(l.Name + ":", "Level " + l.LevelStarted + " Started");
                            else
                                upgradeStatusTable.AddRow(l.Name + ":", "Level " + l.LevelFinished + " Finished");
                        }
                    }

                    if (upgradeStatusTable.Count() > previousCount)
                        upgradeStatusTable.AddRow(" ", " ");

                    previousCount = upgradeStatusTable.Count();
                    
                    foreach (Upgrade u in build.Player.Upgrades)
                    {
                        if (u.ProducedFrom == "Armory" && u.Status != UpgradeStatus.NotStarted)
                            upgradeStatusTable.AddRow(u.Name + ":", u.Status);
                    }

                    if (upgradeStatusTable.Count() > previousCount)
                        upgradeStatusTable.AddRow(" ", " ");

                    previousCount = upgradeStatusTable.Count();
                    
                    foreach (Upgrade u in build.Player.Upgrades)
                    {
                        if (u.ProducedFrom == "Ghost Academy" && u.Status != UpgradeStatus.NotStarted)
                            upgradeStatusTable.AddRow(u.Name + ":", u.Status);
                    }

                    if (upgradeStatusTable.Count() > previousCount)
                        upgradeStatusTable.AddRow(" ", " ");

                    previousCount = upgradeStatusTable.Count();
                    
                    foreach (Upgrade u in build.Player.Upgrades)
                    {
                        if (u.ProducedFrom == "FusionCore" && u.Status != UpgradeStatus.NotStarted)
                            upgradeStatusTable.AddRow(u.Name + ":", u.Status);
                    }

                    if (upgradeStatusTable.Count() > previousCount)
                        upgradeStatusTable.AddRow(" ", " ");

                    previousCount = upgradeStatusTable.Count();
                    
                    foreach (Upgrade u in build.Player.Upgrades)
                    {
                        if (u.ProducedFrom == "Barracks" && u.Status != UpgradeStatus.NotStarted)
                            upgradeStatusTable.AddRow(u.Name + ":", u.Status);
                    }

                    if (upgradeStatusTable.Count() > previousCount)
                        upgradeStatusTable.AddRow(" ", " ");

                    previousCount = upgradeStatusTable.Count();
                    
                    foreach (Upgrade u in build.Player.Upgrades)
                    {
                        if (u.ProducedFrom == "Factory" && u.Status != UpgradeStatus.NotStarted)
                            upgradeStatusTable.AddRow(u.Name + ":", u.Status);
                    }

                    if (upgradeStatusTable.Count() > previousCount)
                        upgradeStatusTable.AddRow(" ", " ");

                    previousCount = upgradeStatusTable.Count();
                    
                    foreach (Upgrade u in build.Player.Upgrades)
                    {
                        if (u.ProducedFrom == "Starport" && u.Status != UpgradeStatus.NotStarted)
                            upgradeStatusTable.AddRow(u.Name + ":", u.Status);
                    }

                    if (upgradeStatusTable.Count() == 0)
                        Console.WriteLine("\nNo upgrades.");
                    else
                        Console.Write(upgradeStatusTable.Output());
                    Console.WriteLine();
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    break;
            }
        }

        public void AddTime(Build build, int timeDifference)
        {
            build.ChangeTime(timeDifference);
            Console.WriteLine("Time has been changed by " + TimeConvert.ToTimeSpan(timeDifference).ToString("g") + ".\n");
        }

        public void SetTime(Build build, int finalTime)
        {
            build.SetTime(finalTime);
            Console.WriteLine("Time has been set to " + build.CurrentTime.ToString(@"hh\:mm\:ss\.ff") + ".\n");
        }

        public void RemoveAction(Build build, int actionIndex)
        {
            string error = null;
            build.RemoveAction(actionIndex, out error);
            if (error == null)
                Console.WriteLine("Action number " + actionIndex + " has been successfully removed.\n");
            else
                Console.WriteLine("ERROR: " + error + "\n");
        }

        public void AddWorker(Build build)
        {
            switch (build.Race)
            {
                case Race.Terran:
                    ProduceSCV scv = new ProduceSCV(build);
                    string error;
                    build.AddAction(scv, out error);
                    if (error == null)
                    {
                        Console.WriteLine("Worker is starting to build now.\n");
                    }
                    else
                    {
                        Console.WriteLine("ERROR: " + error + "\n");
                    }
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    break;
            }
        }

        public void AddUnit(Build build, string unitName)
        {
            unitName = unitName.Replace("-", "");
            unitName = unitName.Replace("_", "");

            string error = null;
            string unit = null;
            bool unitFound = false;
            foreach (Unit u in build.Player.Units)
            {
                unit = u.Name;
                unit = unit.ToLower();
                unit = unit.Replace(" ", "");
                if (unitName == unit)
                {
                    unitFound = true;
                    break;
                }
            }
            if (!unitFound)
            {
                Console.WriteLine("Unrecognized unit \"" + unitName + "\"\n");
                return;
            }
            switch (build.Race)
            {
                case Race.Terran:
                    TerranUnit terranUnit = build.Player.Units.Find(delegate(Unit u)
                    {
                        unit = u.Name;
                        unit = unit.ToLower();
                        unit = unit.Replace(" ", "");
                        return unit == unitName;
                    }) as TerranUnit;
                    ProduceTerranUnit action = new ProduceTerranUnit(build, terranUnit);
                    build.AddAction(action, out error);
                    if (error == null)
                    {
                        Console.WriteLine(terranUnit.Name + " is being created now.\n");
                    }
                    else
                    {
                        Console.WriteLine("ERROR: " + error + "\n");
                    }
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    break;
            }
        }

        public void AddBuilding(Build build, string buildingName, int baseNumber)
        {
            buildingName = buildingName.Replace("-","");
            buildingName = buildingName.Replace("_","");

            string error = null;
            string building = null;
            switch (buildingName)
            {
                case "commandcenter":
                    ProduceMacroCC commandCenter = new ProduceMacroCC(build, build.Map.Bases.ElementAt(baseNumber));
                    build.AddAction(commandCenter, out error);
                    building = "Command Center";
                    break;
                case "refinery":
                    ProduceRefinery refinery = new ProduceRefinery(build, build.Map.Bases.ElementAt(baseNumber));
                    build.AddAction(refinery, out error);
                    building = "Refinery";
                    break;
                case "supplydepot":
                    ProduceTerranBuilding supplyDepot = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Supply Depot"));
                    build.AddAction(supplyDepot, out error);
                    building = "Supply Depot";
                    break;
                case "engineeringbay":
                    ProduceTerranBuilding engineeringBay = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Engineering Bay"));
                    build.AddAction(engineeringBay, out error);
                    building = "Engineering Bay";
                    break;
                case "barracks":
                    ProduceTerranBuilding barracks = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Barracks"));
                    build.AddAction(barracks, out error);
                    building = "Barracks";
                    break;
                case "missileturret":
                    ProduceTerranBuilding missileTurret = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Missile Turret"));
                    build.AddAction(missileTurret, out error);
                    building = "Missile Turret";
                    break;
                case "sensortower":
                    ProduceTerranBuilding sensorTower = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Sensor Tower"));
                    build.AddAction(sensorTower, out error);
                    building = "Sensor Tower";
                    break;
                case "bunker":
                    ProduceTerranBuilding bunker = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Bunker"));
                    build.AddAction(bunker, out error);
                    building = "Bunker";
                    break;
                case "factory":
                    ProduceTerranBuilding factory = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Factory"));
                    build.AddAction(factory, out error);
                    building = "Factory";
                    break;
                case "ghostacademy":
                    ProduceTerranBuilding ghostAcademy = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Ghost Academy"));
                    build.AddAction(ghostAcademy, out error);
                    building = "Ghost Academy";
                    break;
                case "starport":
                    ProduceTerranBuilding starport = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Starport"));
                    build.AddAction(starport, out error);
                    building = "Starport";
                    break;
                case "armory":
                    ProduceTerranBuilding armory = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Armory"));
                    build.AddAction(armory, out error);
                    building = "Armory";
                    break;
                case "fusioncore":
                    ProduceTerranBuilding fusionCore = new ProduceTerranBuilding(build, build.Map.Bases.ElementAt(baseNumber), build.Player.GetBuilding("Fusion Core"));
                    build.AddAction(fusionCore, out error);
                    building = "Fusion Core";
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    return;
            }
            if (error == null)
            {
                Console.WriteLine(building + " is now being constructed.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void AddUpgrade(Build build, string upgradeName)
        {
            upgradeName = upgradeName.Replace("-", "");
            upgradeName = upgradeName.Replace("_", "");
            if ((upgradeName == "vehicleplating" || upgradeName == "shipplating") && build.Expansion != Expansion.WingsOfLiberty)
                upgradeName = "vehicleandshipplating";

            string error = null;
            string upgrade = null;
            bool upgradeFound = false;
            bool levelUpgradeFound = false;
            string upgradeNameString = null;
            foreach (Upgrade u in build.Player.Upgrades)
            {
                upgrade = u.Name;
                upgrade = upgrade.ToLower();
                upgrade = upgrade.Replace(" ", "");
                if (upgrade == upgradeName)
                {
                    upgradeFound = true;
                }
            }
            foreach (LevelUpgrade l in build.Player.LevelUpgrades)
            {
                upgrade = l.Name;
                upgrade = upgrade.ToLower();
                upgrade = upgrade.Replace(" ", "");
                if (upgrade == upgradeName)
                {
                    levelUpgradeFound = true;
                }
            }
            if (!upgradeFound && !levelUpgradeFound)
            {
                Console.WriteLine("Unrecognized upgrade \"" + upgradeName + "\"\n");
                return;
            }
            switch (build.Race)
            {
                case Race.Terran:
                    if (upgradeFound)
                    {
                        Upgrade boolUpgrade = build.Player.Upgrades.Find(delegate(Upgrade u)
                        {
                            upgrade = u.Name;
                            upgrade = upgrade.ToLower();
                            upgrade = upgrade.Replace(" ", "");
                            return upgrade == upgradeName;
                        });
                        ProduceUpgrade boolUpgradeAction = new ProduceUpgrade(build, boolUpgrade);
                        upgradeNameString = boolUpgrade.Name;
                        build.AddAction(boolUpgradeAction, out error);
                    }
                    if (levelUpgradeFound)
                    {
                        LevelUpgrade levelUpgrade = build.Player.LevelUpgrades.Find(delegate(LevelUpgrade l)
                        {
                            upgrade = l.Name;
                            upgrade = upgrade.ToLower();
                            upgrade = upgrade.Replace(" ", "");
                            return upgrade == upgradeName;
                        });
                        ProduceLevelUpgrade levelUpgradeAction = new ProduceLevelUpgrade(build, levelUpgrade);
                        upgradeNameString = levelUpgrade.Name + " Level " + (levelUpgrade.LevelStarted + 1);
                        build.AddAction(levelUpgradeAction, out error);
                    }

                    if (error == null)
                    {
                        Console.WriteLine(upgradeNameString + " is currently being researched.\n");
                    }
                    else
                    {
                        Console.WriteLine("ERROR: " + error + "\n");
                    }
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    break;
            }
        }

        public void FloatCC(Build build, int endingBase, int time)
        {
            FloatCC action = new FloatCC(build, endingBase, time);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Command Center is being floated to the new base.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void SalvageBunker(Build build)
        {
            SalvageBunker action = new SalvageBunker(build);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Bunker is being salvaged.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void AddAddon(Build build, string buildingType, string addonType)
        {
            AddonType addon;
            string buildingName;
            string addonName;
            string building = null;
            build.Player.Buildings.ForEach(delegate(Building b)
            {
                buildingName = b.Name;
                buildingName = buildingName.ToLower();
                buildingName = buildingName.Replace(" ", "");
                if (buildingName == buildingType)
                    building = b.Name;
            });

            if (building == null)
            {
                Console.WriteLine("Unrecognized building \"" + buildingType + "\"\n");
                return;
            }

            switch (addonType)
            {
                case "techlab":
                    addon = AddonType.TechLab;
                    addonName = "Tech Lab";
                    break;
                case "reactor":
                    addon = AddonType.Reactor;
                    addonName = "Reactor";
                    break;
                default:
                    Console.WriteLine("Unrecognized addon \"" + addonType + "\"\n");
                    return;
            }
            ProduceAddon action = new ProduceAddon(build, building, addon);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine(addonName + " is being constructed on a " + building + " now.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void RemoveAddon(Build build, string buildingType, string addonType)
        {
            AddonType addon;
            string buildingName;
            string addonName;
            string building = null;
            build.Player.Buildings.ForEach(delegate(Building b)
            {
                buildingName = b.Name;
                buildingName = buildingName.ToLower();
                buildingName = buildingName.Replace(" ", "");
                if (buildingName == buildingType)
                    building = b.Name;
            });

            if (building == null)
            {
                Console.WriteLine("Unrecognized building \"" + buildingType + "\"\n");
                return;
            }
            switch (addonType)
            {
                case "techlab":
                    addon = AddonType.TechLab;
                    addonName = "Tech Lab";
                    break;
                case "reactor":
                    addon = AddonType.Reactor;
                    addonName = "Reactor";
                    break;
                default:
                    Console.WriteLine("Unrecognized addon \"" + addonType + "\"\n");
                    return;
            }
            RemoveAddon action = new RemoveAddon(build, building, addon);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine(addonName + " is being removed from a " + building + " now.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void AttachAddon(Build build, string buildingType, string addonType)
        {
            AddonType addon;
            string buildingName;
            string addonName;
            string building = null;
            build.Player.Buildings.ForEach(delegate(Building b)
            {
                buildingName = b.Name;
                buildingName = buildingName.ToLower();
                buildingName = buildingName.Replace(" ", "");
                if (buildingName == buildingType)
                    building = b.Name;
            });

            if (building == null)
            {
                Console.WriteLine("Unrecognized building \"" + buildingType + "\"\n");
                return;
            }
            switch (addonType)
            {
                case "techlab":
                    addon = AddonType.TechLab;
                    addonName = "Tech Lab";
                    break;
                case "reactor":
                    addon = AddonType.Reactor;
                    addonName = "Reactor";
                    break;
                default:
                    Console.WriteLine("Unrecognized addon \"" + addonType + "\"\n");
                    return;
            }
            AttachAddon action = new AttachAddon(build, building, addon);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Idle " + addonName + " is being attached to a " + building + " now.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void Orbital(Build build)
        {
            UpgradeCC action = new UpgradeCC(build, CommandCenterUpgrade.OrbitalCommand);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Command Center is being upgraded into an Orbital Command.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void Planetary(Build build)
        {
            UpgradeCC action = new UpgradeCC(build, CommandCenterUpgrade.PlanetaryFortress);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Command Center is being upgraded into a Planetary Fortress.");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void SupplyCalldown(Build build)
        {
            CalldownSupply action = new CalldownSupply(build);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Calling down extra supplies.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void Scan(Build build)
        {
            Scan action = new Scan(build);
            string error = null;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Calling down a scanner sweep.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void Creep(Build build)
        {
            Console.WriteLine("This action's implementation is not finished.\n");
        }

        public void Chronoboost(Build build)
        {
            Console.WriteLine("This action's implementation is not finished.\n");
        }

        public void ConvertWarpgate(Build build)
        {
            Console.WriteLine("This action's implementation is not finished.\n");
        }

        public void Expand(Build build, int workerBaseNumber, int baseNumber)
        {
            Base workerBase = build.Map.Bases.ElementAt(workerBaseNumber);
            string error = null;
            switch (build.Race)
            {
                case Race.Terran:
                    TerranExpand terran = new TerranExpand(build, workerBase, baseNumber);
                    build.AddAction(terran, out error);
                    if (error == null)
                    {
                        Console.WriteLine("Base #" + baseNumber + " is being taken currently.\n");
                    }
                    else
                    {
                        Console.WriteLine("ERROR: " + error + "\n");
                    }
                    break;
                default:
                    Console.WriteLine("This action's implementation is not finished.\n");
                    break;
            }
        }

        public void TransferWorker(Build build, int fromBase, int toBase, int time)
        {
            Base startingBase = build.Map.Bases.ElementAt(fromBase);
            Base endingBase = build.Map.Bases.ElementAt(toBase);
            TransferWorker action = new TransferWorker(startingBase, endingBase, time);
        }

        public void WorkerToGas(Build build, int baseNumber)
        {
            MineralToGas action = new MineralToGas(build, build.Map.Bases.ElementAt(baseNumber));
            string error;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Moving one worker from minerals to gas.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void WorkerToMinerals(Build build, int baseNumber)
        {
            GasToMineral action = new GasToMineral(build, build.Map.Bases.ElementAt(baseNumber));
            string error;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Moving one worker from gas to minerals.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void IdleWorker(Build build, int baseNumber, int time)
        {
            IdleWorker action = new IdleWorker(build, build.Map.Bases.ElementAt(baseNumber), time);
            string error;
            build.AddAction(action, out error);
            if (error == null)
            {
                Console.WriteLine("Idling one worker from Base #" + baseNumber + " for " + time + " hundredths of a second.\n");
            }
            else
            {
                Console.WriteLine("ERROR: " + error + "\n");
            }
        }

        public void Help(Build build)
        {
            Console.WriteLine("\n **** Commands ****");
            Console.WriteLine("\nhelp or commands\nShows this list");
            Console.WriteLine("\nstatus\nShows the resource status of the simulation including current time, minerals,\ngas, supply, and worker count.");
            Console.WriteLine("\nstatus-bases\nShows the status of bases in the simulation including worker count and mineral/gas layout.");
            Console.WriteLine("\nstatus-buildings\nShows the count of buildings and how many are producing in the simulation.");
            Console.WriteLine("\nstatus-units\nShows the count of units in the simulation.");
            Console.WriteLine("\nstatus-upgrades\nShows the status of upgrades in the simulation.");
            Console.WriteLine("\nadd-time (time)\nAdds time in hundredths of a second to the simulation.");
            Console.WriteLine("\nset-time (time)\nSets the time in hundredths of a second of the simulation.");
            Console.WriteLine("\nadd-worker\nTrains a worker.");
            Console.WriteLine("\nadd-unit (unitname)\nTrains a unit of the specified type.");
            Console.WriteLine("\nadd-building (building) (base#)\nBuilds a building of the specified type at the specified base.\nNOTE: The base number starts with the main base as 0.");
            Console.WriteLine("\nadd-upgrade (upgradename)\nStart researching an upgrade of the specified type.");
            if (build.Race == Race.Terran)
            {
                Console.WriteLine("\nfloat-cc (endingbase#) (time)\nFloats an existing Command Center or Orbital Command to a new base.");
                Console.WriteLine("\nsalvage-bunker\nSalvages a bunker.");
                Console.WriteLine("\nadd-addon (building) (addontype)\nAdds a new addon to the building of the specified type.");
                Console.WriteLine("\nremove-addon (building) (addontype)\nRemoves an addon from the building of the specified type.");
                Console.WriteLine("\nattach-addon (building) (addontype)\nAttachs an abandoned addon to the building of the specified type.");
                Console.WriteLine("\norbital\nUpgrades a Command Center into an Orbital Command.");
                Console.WriteLine("\nplanetary\nUpgrades a Command Center into a Planetary Fortress.");
                Console.WriteLine("\nsupplycalldown\nUses Orbital Command energy to use Calldown: Extra Supplies.");
                Console.WriteLine("\nscan\nUses Orbital Command energy to use Calldown: Scanner Sweep.");
            }
            if (build.Race == Race.Zerg)
            {
                Console.WriteLine("\ncreep\nUses Queen energy to create a Creep Tumor.");
            }
            if (build.Race == Race.Protoss)
            {
                Console.WriteLine("\nchronoboost\nUses Chronoboost (arguments will be needed).");
                Console.WriteLine("\nconvert-wg\nConverts a Gateway into a Warpgate.");
            }
            Console.WriteLine("\nexpand (workerbase#) (base#)\nExpands to the base specified.");
            Console.WriteLine("\nrally-workers (mainbuilding#) (base#)\nRallies all newly created workers from the specified building to the specified\nbase.");
            Console.WriteLine("\ntransfer-worker (frombase#) (tobase#) (time)\nTransfers a worker from one base to another.");
            Console.WriteLine("\nworker-to-minerals (base#)\nAdds a worker from gas to minerals at the specified base.");
            Console.WriteLine("\nworker-to-gas (base#)\nAdds a worker from minerals to gas at the specified base.");
            Console.WriteLine("\nidle-worker (base#) (time)\nIdles a worker at the specified for the specified time in hundredths of a\nsecond. (Used to simulate scouting.)\n");
        }

        static void Main(string[] args)
        {
            Program Program = new Program();
            string raceString = "quit";

            raceString = Program.RaceSelect();

            if (raceString == "quit" || raceString == "exit")
            {
                return;
            }
            Race race = Race.Terran;

            // This portion will be moved to another method for selecting the map, once we're at the point where we will need to test that.
            MineralFlags[] flagArray = new MineralFlags[] {MineralFlags.FarPatch, MineralFlags.ClosePatch, 
                                                           MineralFlags.FarPatch, MineralFlags.FarPatch,
                                                           MineralFlags.FarPatch, MineralFlags.ClosePatch,
                                                           MineralFlags.FarPatch, MineralFlags.FarPatch };
            int[] gasArray = new int[] { 2, 0, 0 };
            Expansion expansion = Expansion.HeartOfTheSwarm;
            Build myBuild = new Build(race, expansion, flagArray, gasArray);
            int[] mineralArray = new int[] { 4, 0, 4, 0 };
            myBuild.Map.AddBase(mineralArray, gasArray);
            myBuild.Map.AddBase(mineralArray, gasArray);
            myBuild.Map.AddBase(mineralArray, gasArray);

            Program.ReadCommands(myBuild);
        }
    }
}
